/*
 * @Author: hongbin
 * @Date: 2023-03-21 14:17:25
 * @LastEditors: hongbin
 * @LastEditTime: 2023-03-22 20:16:43
 * @Description: collider
 */
import { FC } from "react";
import * as THREE from "three";
import { Octree } from "three/examples/jsm/math/Octree.js";
import { Capsule } from "three/examples/jsm/math/Capsule.js";
import Layout from "@/src/components/Three/Layout";
import { ThreeHelper } from "@/src/ThreeHelper";

interface IProps {}

const Index: FC<IProps> = () => {
    return (
        <Layout
            title={"八叉树返回碰撞物体"}
            init={init}
            desc="修改八叉树源码让每个三角面绑定所属物体"
        />
    );
};

export default Index;

function init(helper: ThreeHelper) {
    helper.addAxis();
    helper.addStats();
    helper.camera.position.set(0, 1, 1);
    helper.controls.target.set(0, 0.7, -1);
    helper.frameByFrame();
    helper.addGUI();
    helper.useRoomEnvironment();

    const GRAVITY = 0.3;

    const worldOctree = new Octree();

    const playerCollider = new Capsule(
        new THREE.Vector3(0, 0.35, -1),
        new THREE.Vector3(0, 1, -1),
        0.35
    );

    const playerVelocity = new THREE.Vector3();
    const playerDirection = new THREE.Vector3();

    let playerOnFloor = false;
    const keyStates = {} as any;

    document.addEventListener("keydown", (event) => {
        keyStates[event.code] = true;
    });

    document.addEventListener("keyup", (event) => {
        keyStates[event.code] = false;
    });

    const prev = playerCollider.end.clone();

    function playerCollisions() {
        const result = worldOctree.capsuleIntersect(playerCollider);

        playerOnFloor = false;

        if (result) {
            playerOnFloor = result.normal.y > 0;

            playerCollider.translate(
                result.normal.multiplyScalar(result.depth)
            );
        }
    }

    function updatePlayer(deltaTime: number) {
        prev.copy(playerCollider.end);

        if (!playerOnFloor) {
            playerVelocity.y -= GRAVITY;
            // small air resistance
        }

        // playerVelocity.addScaledVector(playerVelocity, damping);

        const deltaPosition = playerVelocity.clone();
        playerCollider.translate(playerVelocity);

        playerCollisions();
        prev.sub(playerCollider.end);
        helper.camera.position.sub(prev);
        helper.controls.target.sub(prev);
    }

    function getForwardVector() {
        helper.camera.getWorldDirection(playerDirection);
        playerDirection.y = 0;
        playerDirection.normalize();

        return playerDirection;
    }

    function getSideVector() {
        helper.camera.getWorldDirection(playerDirection);
        playerDirection.y = 0;
        playerDirection.normalize();
        playerDirection.cross(helper.camera.up);

        return playerDirection;
    }

    function controls(deltaTime: number) {
        // gives a bit of air control
        const speedDelta = playerOnFloor ? 0.1 : 0.07;
        const angle = helper.controls.getAzimuthalAngle();
        const v = new THREE.Vector3();
        if (keyStates["KeyW"]) {
            v.z -= speedDelta;
            v.applyAxisAngle(THREE.Object3D.DefaultUp, angle);
            playerVelocity.add(v);
        }

        if (keyStates["KeyS"]) {
            v.z += speedDelta;
            v.applyAxisAngle(THREE.Object3D.DefaultUp, angle);
            playerVelocity.add(v);
        }

        if (keyStates["KeyA"]) {
            v.x -= speedDelta;
            v.applyAxisAngle(THREE.Object3D.DefaultUp, angle);
            playerVelocity.add(v);
        }

        if (keyStates["KeyD"]) {
            v.x += speedDelta;
            v.applyAxisAngle(THREE.Object3D.DefaultUp, angle);
            playerVelocity.add(v);
        }

        if (playerOnFloor) {
            if (keyStates["Space"]) {
                playerVelocity.y = 3.5;
            }
        }
    }

    let compute = false;
    helper.loadGltf("/models/collision-world.glb").then((gltf) => {
        helper.scene.add(gltf.scene);
        worldOctree.fromGraphNode(gltf.scene);
        compute = true;
    });

    helper.animation(() => {
        if (!compute) return;
        const deltaTime = helper.clock.getDelta();

        controls(deltaTime);

        updatePlayer(deltaTime);
        // 需要一个方向的力 在下坡的时候不抖动这行至关重要
        // 下坡时这个力的方向就是下坡的方向 在这个力的作用下下坡动作不至于一闪一闪的
        playerVelocity.addScaledVector(playerVelocity, -0.6);
        return;
        playerVelocity.x = 0;
        playerVelocity.y /= 1.08;
        if (!playerOnFloor) {
        } else {
            // playerVelocity.y = 0;
        }
        playerVelocity.z = 0;
    });
}
